package com.jboom.mongocrud.service;

import com.jboom.mongocrud.repository.entity.LogisticsOrder;
import com.jboom.mongocrud.repository.entity.OrderItem;
import com.mongodb.BasicDBObject;
import com.mongodb.client.result.UpdateResult;
import jakarta.annotation.Resource;
import org.springframework.data.geo.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class LogisticsOrderService {

    @Resource
    private MongoTemplate mongoTemplate;

    /**
     * 新增订单
     *
     * @param order
     * @return
     */
    public Long addOrder(LogisticsOrder order) {
        return mongoTemplate.insert(order).getOrderId();
    }

    /**
     * 操作员操作，发货、揽收、配送、签收等操作
     *
     * @param orderId
     * @param operator
     * @param longitude
     * @param latitude
     * @param status
     * @return
     */
    public boolean operateOrder(Long orderId, String operator, double longitude, double latitude, Integer status) {
        Criteria criteria = Criteria.where("orderId").is(orderId);
        Query query = new Query(criteria);
        Update update = new Update().set("operator", operator)
                .set("location", new GeoJsonPoint(longitude, latitude))
                .set("status", status);
        UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class);
        return result.getMatchedCount() != 0;
    }

    /**
     * 修改收货地址
     * 修改嵌套子文档字段
     *
     * @param orderId
     * @return
     */
    public boolean modifyReceiveAddress(Long orderId, String province, String city,
                                        String counties, String detail, String postCode) {
        Criteria criteria = Criteria.where("_id").is(orderId);
        Query query = new Query(criteria);
        Update update = new Update();
        Optional.ofNullable(province).ifPresent(p -> update.set("address.province", p));
        Optional.ofNullable(city).ifPresent(c -> update.set("address.city", c));
        Optional.ofNullable(counties).ifPresent(c -> update.set("address.counties", c));
        Optional.ofNullable(detail).ifPresent(d -> update.set("address.detail", d));
        Optional.ofNullable(postCode).ifPresent(p -> update.set("address.postCode", p));
        return mongoTemplate.updateFirst(query, update, LogisticsOrder.class).getMatchedCount() > 0;
    }

    /**
     * 更新货物信息
     * 更新嵌套数组对象字段信息
     *
     * @param orderId
     * @param itemId
     * @param weight
     * @param price
     * @param info
     * @return
     */
    public boolean modifyItem(Long orderId, Long itemId, BigDecimal weight, BigDecimal price, String info) {
        Criteria criteria = Criteria.where("_id").is(orderId)
                .and("items.itemId").is(itemId);
        Query query = new Query(criteria);
        Update update = new Update();
        Optional.ofNullable(weight).ifPresent(w -> update.set("items.$.weight", w));
        Optional.ofNullable(price).ifPresent(p -> update.set("items.$.price", p));
        Optional.ofNullable(info).ifPresent(i -> update.set("items.$.info", i));

        UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class);
        return result.getMatchedCount() != 0;
    }

    /**
     * 添加货物
     * 嵌套数组添加元素
     *
     * @param orderId
     * @param item
     * @return
     */
    public boolean addItem(Long orderId, OrderItem item) {
        Criteria criteria = Criteria.where("_id").is(orderId);
        Query query = new Query(criteria);
        Update update = new Update();
        // 不重复就用addToSet
        update.push("items", item);
        UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class);
        return result.getMatchedCount() != 0;
    }

    /**
     * 清除货物
     * 嵌套数组删除元素
     *
     * @param orderId
     * @param itemId
     * @return
     */
    public boolean removeItem(Long orderId, Long itemId) {
        Criteria criteria = Criteria.where("_id").is(orderId);
        Query query = new Query(criteria);
        BasicDBObject dbObject = new BasicDBObject();
        dbObject.put("itemId", itemId);
        Update update = new Update();
        update.pull("items", dbObject);
        UpdateResult result = mongoTemplate.updateFirst(query, update, LogisticsOrder.class);
        return result.getMatchedCount() != 0;
    }

    /**
     * 统计用户每个状态的订单数
     *
     * @param userId
     * @return
     */
    public void userStatusOrderNum(Long userId) {
        AggregationOperation match = Aggregation.match(Criteria.where("userId").is(userId));
        AggregationOperation group = Aggregation.group("status").count().as("orderNum");
        Aggregation aggregation = Aggregation.newAggregation(match, group);
        AggregationResults<Map> aggregate = mongoTemplate.aggregate(aggregation, LogisticsOrder.class, Map.class);
        for (Map map : aggregate) {
            System.out.println(map);
        }
    }


    /**
     * mongodb一个集合最好只设计一个地理索引，多个会冲突
     * 查询指定经纬度与数据的距离
     *
     * @param orderId
     * @param longitude 收货地精度
     * @param latitude  收货地纬度  -- 用于计算距离
     * @return
     */
    public LogisticsOrder getOrder(Long orderId, double longitude, double latitude) {
        Criteria criteria = Criteria.where("_id").is(orderId);
        Query query = new Query(criteria);
        NearQuery nearQuery = NearQuery
                .near(new GeoJsonPoint(longitude, latitude), Metrics.MILES)
                .query(query);
        GeoResults<LogisticsOrder> results = mongoTemplate.geoNear(nearQuery, LogisticsOrder.class);
        for (GeoResult<LogisticsOrder> result : results) {
            LogisticsOrder content = result.getContent();
            double distance = result.getDistance().getValue();
            content.setDistance(distance);
        }
        // 处理距离
        return results.getContent().get(0).getContent();
    }

    /**
     * 获取用户 range 公里范围(km)内的订单
     *
     * @param userId
     * @param longitude
     * @param latitude
     * @param range
     * @return
     */
    public List<LogisticsOrder> getUserRangeOrder(Long userId, double longitude, double latitude, Integer range) {
        GeoJsonPoint point = new GeoJsonPoint(longitude, latitude);
        Distance distance = new Distance(range, Metrics.KILOMETERS);
        // 圆圈范围内
        Circle circle = new Circle(point, distance);
        Criteria criteria = Criteria.where("userId").is(userId)
                .and("location").withinSphere(circle);
        Query query = new Query(criteria);
        return mongoTemplate.find(query, LogisticsOrder.class);
    }
}
